#include "IManager.h"
#include <ctime>


IManager::IManager(QGraphicsScene * scene): m_ptr_scene(scene), m_timestamp(-1), game_status(GAME_SETTING)
{ }


IManager::~IManager()
{
    if (m_ptr_gameData != NULL)
        delete m_ptr_gameData;
}


void IManager::slotCreateMap(int width, int height, const QString& map, int health, int motion_speed, int bullet_speed, int fire_rate)
{
//    if (m_ptr_gameData != NULL)
//        return;
    qDebug()<<"MAP For the client: "<<map;
    game_status = GAME_MAP_REC;

   // m_ptr_gameData = new GameData();
    m_ptr_gameData->field = new GameField(width, height, map, m_ptr_scene);

    m_max_health = health;

    m_motion_speed = motion_speed;
    m_update_time = CLOCKS_PER_SEC / m_motion_speed;

    m_bullet_speed = bullet_speed;
    if (CLOCKS_PER_SEC / m_bullet_speed < m_update_time)
        m_update_time = CLOCKS_PER_SEC / m_bullet_speed;

    m_fire_rate = fire_rate;
    if (CLOCKS_PER_SEC / m_fire_rate < m_update_time)
        m_update_time = CLOCKS_PER_SEC / m_fire_rate;

    m_update_time /= 4;
    if (m_update_time == 0)
        m_update_time = 1;
}


bool IManager::testNickname(QString &nick)
{
    if (nick.length() < MIN_NICK_LENGTH || nick.length() > MAX_NICK_LENGTH)
        return false;

    for (int i = 0; i < nick.length(); i++)
    {
        if (!(nick.at(i) >= 'a' && nick.at(i) <= 'z'
                || nick.at(i) >= 'A' && nick.at(i) <= 'Z'
                || nick.at(i) >= '0' && nick.at(i) <= '9'))
            return false;
    }

    for (std::list<Tank>::iterator iter = m_ptr_gameData->tanks.begin(); iter !=  m_ptr_gameData->tanks.end(); iter++)
    {
        if (nick == iter->GetNick())
            return false;
    }

    return true;
}


void IManager::advance(long timestamp, bool& need_snapshot)
{
    need_snapshot = false;

    if (timestamp <= m_timestamp)
        return;

    long diff = timestamp - m_timestamp;
    if (diff > m_update_time)
        diff = m_update_time;

    m_erase_bullets.clear();

    // 1: Move tanks
    for (std::list<Tank>::iterator iter = m_ptr_gameData->tanks.begin(); iter != m_ptr_gameData->tanks.end(); ++iter)
    {
       // qDebug()<< iter->GetCurrentAction() <<"  Action of tank";
        if (iter->GetHealth() > 0)// && iter->GetCurrentAction() != 'S')
        {
            if (iter->GetCurrentAction() == 'M')
            {
                // Move tank forward:
                if (iter->advanceAction(diff))
                {
                    std::map<int, Player>::iterator pl = m_players.find(iter->GetID());
                    if (!pl->second.queueEmpty())
                    {
                        if (pl->second.nextAction() == 'S')
                        {
                            iter->SetProgress(0.0);
                        }
                        iter->SetCurrentAction(pl->second.action);
                    }
                    // Test for possible collisions with walls and other tanks:
                    else if (testCollisionTank(iter->GetID()))
                    {
                        iter->SetProgress(0.0);
                    }
                }
            }
            else
            {
                // Rotate tank
               // qDebug()<<"IManager::advance(long timestamp, bool& need_snapshot) "<<iter->advanceAction(diff);
                if (iter->advanceAction(diff))
                {
                    std::map<int, Player>::iterator pl = m_players.find(iter->GetID());
                    if (!pl->second.queueEmpty())
                    {
                        if (pl->second.nextAction() == 'M')
                        {
                            if (testCollisionTank(iter->GetID()))
                            {
                                iter->SetProgress(0.0);
                            }
                        }
                        iter->SetCurrentAction(pl->second.action);
                    }
                    else
                    {
                        iter->SetProgress(0.0);
                        iter->SetCurrentAction('S');
                        pl->second.action = 'S';
                    }
                }
            }
        }
    }

    // 2: Fire bullets
    for (std::list<Tank>::iterator iter = m_ptr_gameData->tanks.begin(); iter != m_ptr_gameData->tanks.end(); ++iter)
    {
        if (iter->GetHealth() > 0 && iter->GetCurrentAction() != 'R' && iter->GetCurrentAction() != 'L')
        {
            std::map<int, Player>::iterator pl = m_players.find(iter->id);
            if (pl->second.firing)
            {
                long elapsed = m_timestamp + diff - pl->second.last_fired;
                if (elapsed >= CLOCKS_PER_SEC / m_fire_rate)
                {
                    Bullet bullet(++max_bullet, m_bullet_speed, iter->GetPosX(), iter->GetPosY(), iter->GetDirection(), iter->GetProgress());
                    bullet.addToScene(m_ptr_scene);
                    switch (iter->GetDirection())
                    {
                    case 'U':
                        bullet.SetPosX(bullet.GetPosX()+2);
                        break;
                    case 'D':
                        bullet.SetPosX(bullet.GetPosX()+2);
                        bullet.SetPosY(bullet.GetPosY()+4);
                        break;
                    case 'L':
                        bullet.SetPosY(bullet.GetPosY()+2);
                        break;
                    default:
                        bullet.SetPosX(bullet.GetPosX()+4);
                        bullet.SetPosY(bullet.GetPosY()+2);
                    }
                    m_ptr_gameData->bullets.push_back(bullet);
                    pl->second.last_fired = m_timestamp + elapsed;
                }
            }
        }
    }

    // 3: Move bullets
    for (std::list<Bullet>::iterator iter = m_ptr_gameData->bullets.begin(); iter != m_ptr_gameData->bullets.end(); ++iter)
    {
        if (m_erase_bullets.find(iter->GetID()) == m_erase_bullets.end())
        {
            // Move bullet forward:
            iter->advanceAction(diff);

            // Test for possible collisions:
            int tank_id1, tank_id2, bullet_id1;
            if (testCollisionBullet(iter->GetID(), tank_id1, tank_id2, bullet_id1))
            {
                m_erase_bullets.insert(iter->GetID());
                if (tank_id1 >= 0)
                {
                    need_snapshot = true;
                    demageTank(tank_id1);
                }
                if (tank_id2 >= 0)
                {
                    need_snapshot = true;
                    demageTank(tank_id2);
                }
                if (bullet_id1 >= 0)
                    m_erase_bullets.insert(bullet_id1);
            }
        }
    }

    // 4: Erase disappeared bullets:
    for (std::set<int>::iterator ptr_id = m_erase_bullets.begin(); ptr_id != m_erase_bullets.end(); ++ptr_id)
    {
        for (std::list<Bullet>::iterator iter = m_ptr_gameData->bullets.begin(); iter != m_ptr_gameData->bullets.end(); ++iter)
        {
            if ((*ptr_id) == iter->GetID())
            {
                m_ptr_gameData->bullets.erase(iter);
                break;
            }
        }
    }

    m_erase_bullets.clear();

    m_timestamp += diff;
}


bool IManager::testCollisionBullet(int bullet_id, int &tank_id1, int &tank_id2, int bullet_id1)
{
    std::list<Bullet>::iterator bullet = m_ptr_gameData->bullets.begin();
    while (bullet->GetID() != bullet_id)
        ++bullet;

    tank_id1 = -1;
    tank_id2 = -1;
    bullet_id1 = -1;

    // Test borders and walls:
    double x = bullet->GetPosX();
    double y = bullet->GetPosY();
    switch (bullet->GetDirection())
    {
    case 'R':
        x += bullet->GetProgress() / 100.0;
        break;
    case 'L':
        x -= bullet->GetProgress() / 100.0;
        break;
    case 'U':
        y -= bullet->GetProgress() / 100.0;
        break;
    default:
        y += bullet->GetProgress() / 100.0;
    }
    if (x < 0 || y < 0 || x > m_ptr_gameData->field->GetWidth() || y > m_ptr_gameData->field->GetHeight())
        return true;
    {
        int i1 = bullet->GetPosX() - 1;
        int j1 = bullet->GetPosY() - 1;
        int i2 = bullet->GetPosX();
        int j2 = bullet->GetPosY();
        switch (bullet->GetDirection())
        {
        case 'R':
            i1++;
            break;
        case 'L':
            i2--;
            break;
        case 'U':
            j2--;
            break;
        default:
            j1++;
        }
        if (m_ptr_gameData->field->GetCell(i1, j1) != NULL)
        {
            if (m_ptr_gameData->field->GetCell(i1, j1)->GetType() == 'W')
            {
                if (m_ptr_gameData->field->GetCell(i2, j2) != NULL)
                {
                    if (m_ptr_gameData->field->GetCell(i2, j2)->GetType() == 'W')
                        return true;
                }
            }
        }
    }

    // Test collisions with other bullets:
    for (std::list<Bullet>::iterator iter = m_ptr_gameData->bullets.begin(); iter != m_ptr_gameData->bullets.end(); ++iter)
    {
        if (iter == bullet || m_erase_bullets.find(iter->GetID()) != m_erase_bullets.end())
            continue;
        if (iter->GetDirection() == 'R' && bullet->GetDirection() == 'L')
        {
            if (iter->GetPosX() + 1 == bullet->GetPosX() && iter->GetPosY() == bullet->GetPosY()
                    && iter->GetProgress() + bullet->GetProgress() >= 100
                    || iter->GetPosX() == bullet->GetPosX() && iter->GetPosY() == bullet->GetPosY())
            {
                bullet_id1 = iter->GetID();
                return true;
            }
        }
        else if (iter->GetDirection() == 'L' && bullet->GetDirection() != 'R')
        {
            if (iter->GetPosX() - 1 == bullet->GetPosX() && iter->GetPosY() == bullet->GetPosY()
                    && iter->GetProgress() + bullet->GetProgress() >= 100
                    || iter->GetPosX() == bullet->GetPosX() && iter->GetPosY() == bullet->GetPosY())
            {
                bullet_id1 = iter->GetID();
                return true;
            }
        }
        else if (iter->GetDirection() == 'D' && bullet->GetDirection() != 'U')
        {
            if (iter->GetPosY() + 1 == bullet->GetPosY() && iter->GetPosX() == bullet->GetPosX()
                    && iter->GetProgress() + bullet->GetProgress() >= 100
                    || iter->GetPosX() == bullet->GetPosX() && iter->GetPosY() == bullet->GetPosY())
            {
                bullet_id1 = iter->GetID();
                return true;
            }
        }
        else if (iter->GetDirection() == 'U' && bullet->GetDirection() != 'D')
        {
            if (iter->GetPosY() - 1 == bullet->GetPosY() && iter->GetPosX() == bullet->GetPosX()
                    && iter->GetProgress() + bullet->GetProgress() >= 100
                    || iter->GetPosX() == bullet->GetPosX() && iter->GetPosY() == bullet->GetPosY())
            {
                bullet_id1 = iter->GetID();
                return true;
            }
        }
    }

    // Test collisions with tanks:
    bool collision = false;
    for (std::list<Tank>::iterator iter = m_ptr_gameData->tanks.begin(); iter != m_ptr_gameData->tanks.end(); ++iter)
    {
        if (iter->GetHealth() == 0)
            continue;
        double x1 = iter->GetPosX();
        double y1 = iter->GetPosY();
        if ('M' == iter->GetCurrentAction())
        {
            switch (iter->GetDirection())
            {
            case 'R':
                x1 += iter->GetProgress() / 100.0;
                break;
            case 'L':
                x1 -= iter->GetProgress() / 100.0;
                break;
            case 'U':
                y1 -= iter->GetProgress() / 100.0;
                break;
            default:
                y1 += iter->GetProgress() / 100.0;
            }
        }
        if (x <= x1+4 && x1 <= x && y <= y1+4 && y1 <= y)
        {
            if (tank_id1 == -1)
            {
                collision = true;
                tank_id1 = iter->GetID();
            }
            else
            {
                tank_id2 = iter->GetID();
                break;
            }
        }
    }

    return collision;
}


bool IManager::testCollisionTank(int tank_id)
{
    std::list<Tank>::iterator tank = m_ptr_gameData->tanks.begin();
    while (tank->GetID() != tank_id)
        ++tank;

    if (tank->GetCurrentAction() != 'M')
        return false;

    // Test walls:
    int x = tank->GetPosX();
    int y = tank->GetPosY();
    int w = 4;
    int h = 4;
    switch (tank->GetDirection())
    {
    case 'L':
        x--;
        if (x < 0)
            return true;
        w++;
        break;
    case 'R':
        w++;
        if (x+w >= m_ptr_gameData->field->GetWidth())
            return true;
        break;
    case 'U':
        y--;
        if (y < 0)
            return true;
        h++;
        break;
    default:
        h++;
        if (y+h >= m_ptr_gameData->field->GetHeight())
            return true;
    }
    for (int i = 0; i < w; ++i)
    {
        for (int j = 0; j < h; ++j)
        {
            if ('W' == m_ptr_gameData->field->GetCell(i+x, j+y)->GetType())
                return true;
        }
    }

    // Test other tanks:
    for (std::list<Tank>::iterator iter = m_ptr_gameData->tanks.begin(); iter != m_ptr_gameData->tanks.end(); ++iter)
    {
        if (iter != tank && iter->GetHealth() > 0)
        {
            int x1 = iter->GetPosX();
            int y1 = iter->GetPosY();
            int w1 = 4;
            int h1 = 4;
            if ('M' == iter->GetCurrentAction())
            {
                switch (iter->GetDirection())
                {
                case 'L':
                    x1--;
                    w1++;
                    break;
                case 'R':
                    w1++;
                    break;
                case 'U':
                    y1--;
                    h1++;
                    break;
                default:
                    h1++;
                }
                if (x < x1+w1 && x1 < x+w && y < y1+h1 && y1 < y+h
                        && tank->GetProgress() < iter->GetProgress())
                    return true;
            }
        }
    }

    return false;
}


bool IManager::demageTank(int tank_id)
{
    std::list<Tank>::iterator iter;
    for (iter = m_ptr_gameData->tanks.begin(); iter != m_ptr_gameData->tanks.end(); ++iter)
    {
        if (iter->GetID() == tank_id)
            break;
    }
    if (iter == m_ptr_gameData->tanks.end())
        return false;
    if (iter->GetHealth() == 0)
        return true;
    iter->SetHealth(iter->GetHealth()-1);
    return (iter->GetHealth() == 0);
}
